package com.wecan.oauth.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.wecan.api.saasdata.UserData;
import com.wecan.oauth.DTO.security.LoginDTO;
import com.wecan.oauth.DTO.security.PhonePasswordLoginDTO;
import com.wecan.oauth.DTO.user.*;
import com.wecan.base.PO.User;
import com.wecan.base.VO.UserVO;
import com.wecan.base.constants.PropertyConstants;
import com.wecan.base.constants.RedisKey;
import com.wecan.base.constants.WeChatKeys;
import com.wecan.base.enums.UserStatus;
import com.wecan.base.exception.AppException;
import com.wecan.base.model.Result;
import com.wecan.base.utils.AESUtil;
import com.wecan.cache.utils.RedisUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.IOException;

/**
 * @Author liuzhihao
 * @Date 2022.01.20
 * @Version ${version}
 * @Description ${description}
 */
@RestController
@RequestMapping(value = "/")
@Tag(name = "安全接口")
@Validated
public class SecurityController {

    @Autowired
    UserData userData;

    @Autowired
    RedisUtil redisUtil;

    @Value(PropertyConstants.ANNOTATION_APP_POSITION_SECRET)
    Long positionSecret;

    @Value(PropertyConstants.ANNOTATION_APP_JD_KEY)
    String jdKey;

    @Value(PropertyConstants.ANNOTATION_APP_JD_SECRET)
    String jdSecret;

    @Value(PropertyConstants.ANNOTATION_APP_WECHAT_AUTH_KEY)
    String wechatKey;

    @Value(PropertyConstants.ANNOTATION_APP_WECHAT_AUTH_WEB_KEY)
    String wechatWebKey;

    @Value(PropertyConstants.ANNOTATION_APP_WECHAT_AUTH_SECRET)
    String wechatSecret;

    @Value(PropertyConstants.ANNOTATION_APP_WECHAT_AUTH_WEB_SECRET)
    String wechatWebSecret;

    @Operation(summary = "注册", description = "用户注册",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "用户注册", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "userRegisterDTO", required = true, description = "查询角色参数", schema = @Schema(implementation = UserRegisterDTO.class))
    })
    @PostMapping(value = "/register")
    public Result register(@RequestBody @Valid UserRegisterDTO userRegisterDTO) {
        String phone = userRegisterDTO.getPhone();
        String password = userRegisterDTO.getPassword();
        String validateCode = userRegisterDTO.getValidateCode();
        User selectUser = userData.queryUserByPhone(phone);
        if (selectUser != null) {
            return Result.failure("手机号已经被注册，请登录");
        }
        String encryPassword = AESUtil.encryptSqlField(password);
        String redisValidateCode = (String) redisUtil.get(RedisKey.PHONE_VALIDATE_CODE + phone);
        if (StringUtils.equals(validateCode, redisValidateCode)) {
            User user = new User();
            String randomUUID = AESUtil.generateUuid();
            long phoneNumber = Long.parseLong(phone);
            long positionId = phoneNumber + positionSecret;
            user.setName("用户-" + randomUUID.substring(0, 12));
            user.setPhone(phone);
            user.setPassword(encryPassword);
            user.setPositionId(positionId);
            User newUser = userData.insert(user);
            if (newUser.getId() != null) {
                UserVO userVO = new UserVO();
                userVO.setAvatar(newUser.getAvatar());
                userVO.setBirthday(newUser.getBirthday());
                userVO.setId(newUser.getId());
                userVO.setIntegral(newUser.getIntegral());
                userVO.setName(newUser.getName());
                userVO.setSex(newUser.getSex());
                userVO.setPositionId(newUser.getPositionId());
                userVO.setStatus(newUser.getStatus());
                userVO.setLevel(newUser.getLevel());
                userVO.setIsAuthAvatar(newUser.getIsAuthAvatar());
                redisUtil.del(RedisKey.PHONE_VALIDATE_CODE + phone);
                return Result.success("注册成功", userVO);
            } else {
                return Result.failure("注册失败");
            }
        } else {
            return Result.failure("手机验证码错误");
        }
    }

    @Operation(summary = "手机号登录", description = "用户手机号登录",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "用户手机号登录", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "loginDTO", required = true, description = "手机号登录参数", schema = @Schema(implementation = LoginDTO.class))
    })
    @PostMapping(value = "/pologin")
    public Result poLogin(@RequestBody @Valid LoginDTO loginDTO) {
        String phone = loginDTO.getPhone();
        String validateCode = loginDTO.getValidateCode();
        String redisValidateCode = (String) redisUtil.get(RedisKey.PHONE_VALIDATE_CODE + phone);
        if (!StringUtils.equals(validateCode, redisValidateCode)) {
            return Result.failure("手机验证码不正确");
        }
        User user = userData.queryUserByPhone(phone);
        if (user == null) {
            return Result.failure("用户名或者密码不正确");
        }
        UserStatus userStatus = user.getStatus();
        if (userStatus != UserStatus.ENABLED) {
            return Result.failure("该账号已被" + userStatus.getDesc());
        }
        boolean isLogin = StpUtil.isLogin();
        if (isLogin) {
            StpUtil.checkActivityTimeout();
        } else {
            StpUtil.login(user.getId());
        }
        Long id = user.getId();
        Long positionId = user.getPositionId();
        String token = StpUtil.getTokenValue();
        redisUtil.set(RedisKey.USER_POSITION_ID + id, positionId, RedisKey.USER_POSITION_ID_EXPIRE_TIME);
        UserVO userVO = new UserVO();
        userVO.setAvatar(user.getAvatar());
        userVO.setBirthday(user.getBirthday());
        userVO.setId(user.getId());
        userVO.setIntegral(user.getIntegral());
        userVO.setName(user.getName());
        userVO.setSex(user.getSex());
        userVO.setIsAuthAvatar(user.getIsAuthAvatar());
        userVO.setPositionId(user.getPositionId());
        userVO.setStatus(user.getStatus());
        userVO.setLevel(user.getLevel());
        userVO.setToken(token);
        userVO.setBirthday(user.getBirthday());
        userVO.setIsAuthAvatar(user.getIsAuthAvatar());
        return Result.success("登录成功", userVO);
    }

    @Operation(summary = "用户密码登录", description = "用户密码登录",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "用户密码登录", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "phonePasswordLoginDTO", required = true, description = "手机号登录参数", schema = @Schema(implementation = PhonePasswordLoginDTO.class))
    })
    @PostMapping(value = "/pslogin")
    public Result psLogin(@RequestBody @Valid PhonePasswordLoginDTO phonePasswordLoginDTO) {
        String name = phonePasswordLoginDTO.getName();
        String password = phonePasswordLoginDTO.getPassword();
        User user = userData.queryUserByNameAndPass(name, AESUtil.encryptSqlField(password));
        if (user == null) {
            return Result.failure("用户名或者密码不正确");
        }
        if (user.getStatus() != UserStatus.ENABLED) {
            return Result.failure("该账号已被" + user.getStatus().getDesc());
        }
        Long userId = user.getId();
        StpUtil.login(userId);
        String token = StpUtil.getTokenValue();
        redisUtil.set(RedisKey.USER_POSITION_ID + userId, user.getPositionId(), RedisKey.USER_POSITION_ID_EXPIRE_TIME);
        UserVO userVO = new UserVO();
        userVO.setId(user.getId());
        userVO.setToken(token);
        userVO.setPositionId(user.getPositionId());
        userVO.setLevel(user.getLevel());
        userVO.setName(user.getName());
        userVO.setStatus(user.getStatus());
        userVO.setSex(user.getSex());
        userVO.setIntegral(user.getIntegral());
        userVO.setBirthday(user.getBirthday());
        userVO.setAvatar(user.getAvatar());
        userVO.setIsAuthAvatar(user.getIsAuthAvatar());
        return Result.success("登录成功", userVO);
    }

    /**
     * 微信登录公共逻辑
     * @param url
     * @return
     */
    private Result wechatCommonLogin(String url) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        CloseableHttpClient httpClient1 = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(url);
        CloseableHttpResponse response = null;
        CloseableHttpResponse response1 = null;
        try {
            response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                JSONObject result = JSONObject.parseObject(EntityUtils.toString(entity, "utf-8"));
                if (StringUtils.isEmpty(result.getString("access_token"))) {
                    return Result.failure("微信登录失败");
                }
                String accessToken = result.getString("access_token");
                String openId = result.getString("openid");
                String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId;
                HttpGet httpGet1 = new HttpGet(userInfoUrl);
                response1 = httpClient1.execute(httpGet1);
                entity = response1.getEntity();
                if (entity != null) {
                    result = JSONObject.parseObject(EntityUtils.toString(entity, "utf-8"));
                    if (StringUtils.isEmpty(result.getString("unionid"))) {
                        return Result.failure("微信登录失败");
                    }
                    String name = result.getString("nickname");
                    String wechatId = result.getString("unionid");
                    String avatar = result.getString("headimgurl");
                    User user = new User();
                    user.setWechatId(wechatId);
                    User queryUser = userData.queryUser(user);
                    if (queryUser == null) {
                        User userName = new User();
                        userName.setName(name);
                        User user1 = userData.queryUser(userName);
                        user.setName(name);
                        if (user1 != null) {
                            String randomUUID = AESUtil.generateUuid();
                            user.setName(name + randomUUID.substring(0, 12));
                        }
                        user.setAvatar(avatar);
                        user = userData.insert(user);
                        long id = user.getId();
                        StpUtil.login(id);
                        String token = StpUtil.getTokenValue();
                        UserVO userVO = new UserVO();
                        userVO.setAvatar(user.getAvatar());
                        userVO.setBirthday(user.getBirthday());
                        userVO.setId(user.getId());
                        userVO.setIntegral(user.getIntegral());
                        userVO.setName(user.getName());
                        userVO.setSex(user.getSex());
                        userVO.setPositionId(user.getPositionId());
                        userVO.setStatus(user.getStatus());
                        userVO.setLevel(user.getLevel());
                        userVO.setToken(token);
                        userVO.setIsAuthAvatar(user.getIsAuthAvatar());
                        return Result.success("登录成功", userVO);
                    } else {
                        if (queryUser.getStatus() != UserStatus.ENABLED) {
                            return Result.failure("该账号已被" + queryUser.getStatus().getDesc());
                        }
                        long userId = queryUser.getId();
                        StpUtil.login(userId);
                        String token = StpUtil.getTokenValue();
                        redisUtil.set(RedisKey.USER_POSITION_ID + userId, queryUser.getPositionId(), RedisKey.USER_POSITION_ID_EXPIRE_TIME);
                        UserVO userVO = new UserVO();
                        userVO.setAvatar(queryUser.getAvatar());
                        userVO.setBirthday(queryUser.getBirthday());
                        userVO.setId(queryUser.getId());
                        userVO.setIntegral(queryUser.getIntegral());
                        userVO.setName(queryUser.getName());
                        userVO.setSex(queryUser.getSex());
                        userVO.setPositionId(queryUser.getPositionId());
                        userVO.setStatus(queryUser.getStatus());
                        userVO.setLevel(queryUser.getLevel());
                        userVO.setToken(token);
                        userVO.setIsAuthAvatar(queryUser.getIsAuthAvatar());
                        return Result.success("登录成功", userVO);
                    }
                }
            }
        } catch (ParseException | IOException e) {
            throw new AppException("授权失败");
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (httpClient1 != null) {
                    httpClient1.close();
                }
                if (response != null) {
                    response.close();
                }
                if (response1 != null) {
                    response1.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return Result.failure("微信登录失败");
    }

    @Operation(summary = "微信快捷登录", description = "微信快捷登录",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "微信快捷登录", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "wechatLoginDTO", required = true, description = "微信登录参数", schema = @Schema(implementation = WechatLoginDTO.class))
    })
    @PostMapping(value = "/wechat/login")
    public Result wechatLogin(@RequestBody @Valid WechatLoginDTO wechatLoginDTO) {
        String url = WeChatKeys.AUTH_URL + wechatKey + "&secret=" + wechatSecret + "&code=" + wechatLoginDTO.getCode() + "&grant_type=authorization_code";
        return wechatCommonLogin(url);
    }

    @PostMapping(value = "/wechat/web/login")
    public Result wechatWebLogin(@RequestBody @Valid WechatLoginDTO wechatLoginDTO) {
        String url = WeChatKeys.AUTH_URL + wechatWebKey + "&secret=" + wechatWebSecret + "&code=" + wechatLoginDTO.getCode() + "&grant_type=authorization_code";
        return wechatCommonLogin(url);
    }

    @Operation(summary = "退出登录", description = "退出登录",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "退出登录", content = @Content(mediaType = "application/json"))})
    @PostMapping(value = "/logout")
    public Result logout() {
        String userId = StpUtil.getLoginIdAsString();
        redisUtil.del(RedisKey.USER_POSITION_ID + userId);
        StpUtil.logout();
        return Result.success("退出登录成功");
    }

    @Operation(summary = "绑定手机号", description = "绑定手机号",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "绑定手机号", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "bindPhoneDTO", required = true, description = "绑定手机号参数", schema = @Schema(implementation = BindPhoneDTO.class))
    })
    @PostMapping(value = "/bindphone")
    public Result bindPhone(@RequestBody @Valid BindPhoneDTO bindPhoneDTO) {
        String phone = bindPhoneDTO.getPhone();
        String validateCode = bindPhoneDTO.getValidateCode();
        Long userId = StpUtil.getLoginIdAsLong();
        String redisValidateCode = (String) redisUtil.get(RedisKey.PHONE_VALIDATE_CODE + phone);
        if (!StringUtils.equals(validateCode, redisValidateCode)) {
            return Result.failure("手机验证码错误");
        }
        User wechatUser = userData.queryUserById(userId);
        User user = userData.queryUserByPhone(phone);
        if (user != null) {
            return Result.failure("手机号已被绑定");
        }
        long phoneNumber = NumberUtils.toLong(phone);
        long positionId = phoneNumber + positionSecret;
        wechatUser.setPhone(phone);
        wechatUser.setPositionId(positionId);
        boolean isSuccess = userData.updateInfoById(wechatUser);
        if (isSuccess) {
            redisUtil.del(RedisKey.PHONE_VALIDATE_CODE + phone);
            return Result.success("绑定成功", positionId);
        }
        return Result.failure("手机号绑定失败,请重试");
    }

    @Operation(summary = "验证是否登录", description = "验证是否登录",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "验证是否登录", content = @Content(mediaType = "application/json"))})
    @PostMapping(value = "/vertify")
    public Result<Boolean> isLogin() {
        boolean isLogin = StpUtil.isLogin();
        if (isLogin) {
            return Result.success("已登录", true);
        }
        return Result.failure("未登录", false);
    }


    @Operation(summary = "忘记密码", description = "忘记密码",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "忘记密码", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "userRegisterDTO", required = true, description = "忘记密码参数", schema = @Schema(implementation = UserRegisterDTO.class))
    })
    @PostMapping(value = "/forgetpassword")
    public Result forgetPassword(@RequestBody @Valid UserRegisterDTO userRegisterDTO) {
        String password = userRegisterDTO.getPassword();
        String phone = userRegisterDTO.getPhone();
        String validateCode = userRegisterDTO.getValidateCode();
        String redisValidateCode = (String) redisUtil.get(RedisKey.PHONE_VALIDATE_CODE + phone);
        if (StringUtils.equals(validateCode, redisValidateCode)) {
            if (userData.editPasswordbyPhone(phone, AESUtil.encryptSqlField(password))) {
                redisUtil.del(RedisKey.PHONE_VALIDATE_CODE + phone);
                return Result.success("修改成功");
            }
            return Result.failure("修改信息失败");
        }
        return Result.failure("手机验证码错误");
    }

    @Operation(summary = "修改密码", description = "修改密码",
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = "application/json")),
            responses = {@ApiResponse(description = "修改密码", content = @Content(mediaType = "application/json"))})
    @Parameters({
            @Parameter(name = "editPassDTO", required = true, description = "修改密码参数", schema = @Schema(implementation = EditPassDTO.class))
    })
    @PostMapping(value = "/editpassword")
    public Result editPassword(@RequestBody @Valid EditPassDTO editPassDTO) {
        String oldPassword = editPassDTO.getOldPassword();
        String newPassword = editPassDTO.getNewPassword();
        Long userId = StpUtil.getLoginIdAsLong();
        String password = userData.queryPassword(userId);
        if (StringUtils.equals(oldPassword, newPassword)) {
            if (StringUtils.isEmpty(password)) {
                if (userData.updatePassword(userId, AESUtil.encryptSqlField(newPassword))) {
                    return Result.success("初始化密码成功");
                }
                return Result.failure("初始化密码失败");
            }
            return Result.failure("新密码和旧密码不能相同");
        }
        if (password != null && !StringUtils.equals(oldPassword, AESUtil.decrypt(password))) {
            return Result.failure("原密码不正确，修改信息失败");
        }
        if (userData.updatePassword(userId, AESUtil.encryptSqlField(newPassword))) {
            return Result.success("修改密码成功");
        }
        return Result.failure("修改密码失败");
    }
}
